/**
 * 
 */
package com.xpec.c4.game.utility;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

/**
 * 處理 java class 和 json 字串互轉
 * 
 * @author TW15010
 */
public class JsonConvert {
	private static final Log log = LogFactory.getLog(JsonConvert.class);

	/** 轉成 json 時要額外寫入 class 的名稱，還原時才有依據 */
	private static final String KEY_MAIN_CLASS_NAME = "mainClassName";

	/**
	 * 將 class 轉成 json 字串
	 * 
	 * @param obj
	 *            物件
	 * @return 成功：json 字串<br>
	 *         失敗：null
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static String toJson(Object obj) throws Exception {
		if (obj == null) {
			return null;
		}

		JSONObject json = new JSONObject();

		// 先寫入 class name
		json.put(KEY_MAIN_CLASS_NAME, obj.getClass().getName());

		// 掃一遍 class object 底下的所有欄位
		Class<?> currentClass;
		currentClass = obj.getClass();
		while (currentClass != null) {
			// 取得目前 class 的所有欄位，不包含 parent class
			Field[] fields = currentClass.getDeclaredFields();

			// 掃一遍所有欄位，將欄位的值轉成 json
			for (Field field : fields) {
				if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
					continue;
				}

				// 解除 private field 的存取限制
				field.setAccessible(true);

				String fieldType = field.getType().getSimpleName();
				String fieldName = field.getName();
				Object fieldValue = field.get(obj);

				if (fieldValue == null) {
					json.put(fieldName, null);
					continue;
				}

				// 依照欄位類型不同寫入 json 容器
				switch (fieldType) {
				case "boolean":
					json.put(fieldName, fieldValue);
					break;

				case "String":
					json.put(fieldName, fieldValue);
					break;

				case "int":
					json.put(fieldName, fieldValue);
					break;

				case "BigDecimal": {
					BigDecimal var = (BigDecimal) fieldValue;
					json.put(fieldName, var.toString());
					break;
				}

				case "Long":
					json.put(fieldName, fieldValue);
					break;

				case "long":
					json.put(fieldName, fieldValue);
					break;

				case "Byte":
					json.put(fieldName, fieldValue);
					break;

				case "byte":
					json.put(fieldName, fieldValue);
					break;	
					
					
				case "Calendar": {
					Calendar cal = (Calendar) fieldValue;
					json.put(fieldName, cal.getTimeInMillis());
					break;
				}

				case "Date": {
					Date cal = (Date) fieldValue;
					json.put(fieldName, cal.getTime());
					break;
				}

				case "GUID": {
					GUID guid = (GUID) fieldValue;
					json.put(fieldName, guid.toString());
					break;
				}

				default:
					
					if(field.getType().isEnum()){
						StringBuilder builder = new StringBuilder();
						builder.append(field.getType().getCanonicalName());
						builder.append("_");
						builder.append(fieldName);
						json.put(builder.toString(),fieldValue.toString());
					} else if (log.isErrorEnabled()) {
						log.error("toJson(): field [" + obj.getClass().getSimpleName() + "." + fieldName
								+ "] type [" + fieldType + "] not support.");
					}
					break;
				}
			}

			// 取得 parent class
			currentClass = currentClass.getSuperclass();
		} // while (currentClass != null)

		return json.toString();
	}

	/**
	 * 將 json 字串還原成物件
	 * 
	 * @param jsonString
	 *            物件轉成的 json 字串
	 * @return 成功：還原的物件<br>
	 *         失敗：null
	 * @throws Exception
	 */
	public static Object toObject(String jsonString) throws Exception {
		if (jsonString == null) {
			return null;
		}

		// 將 json 字串轉成 json object
		JSONParser parser = new JSONParser();

		Object tmpObj = null;
		try {
			tmpObj = parser.parse(jsonString);
		} catch (Exception e) {
			// 解析 json 字串失敗
			return null;
		}

		if (tmpObj == null) {
			return null;
		}
		JSONObject json = (JSONObject) tmpObj;

		// 取出物件的原始 class 名稱
		String className = (String) json.get(KEY_MAIN_CLASS_NAME);

		// 用名稱產生出 class 實體
		try {
			tmpObj = Class.forName(className).newInstance();
		} catch (Exception e) {
			return null;
		}

		// 掃整個 class，每一個欄位都向 json obejct 取值
		Class<?> currentClass;
		currentClass = tmpObj.getClass();
		while (currentClass != null) {
			// 取出目前 class 下的所有欄位，不包含 parent class
			Field[] fields = currentClass.getDeclaredFields();

			// 每個欄位都向 json object 取值
			for (Field field : fields) {
				if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
					continue;
				}

				// 解除 private field 的存取限制
				field.setAccessible(true);

				String fieldType = field.getType().getSimpleName();
				String fieldName = field.getName();

				// json object 沒有對應欄位名稱的值
				if (!json.containsKey(fieldName)) {

					Class<?> type = field.getType(); 
					if(type.isEnum()){
						Object valueObject = json.get(type.getCanonicalName()+ "_" + fieldName);
						Class<? extends Enum> fieldClass = (Class<? extends Enum>) type;
						Enum<?> enumValue = Enum.valueOf(fieldClass, valueObject.toString());
						field.set(tmpObj, enumValue);	
					} else if (log.isErrorEnabled()) {
						log.error("toObject(): field [" + tmpObj.getClass()
								.getSimpleName() + "." + fieldName + "] not found in json.");
					}
					
					continue;
				}

				// 從 json object 中取出對應欄位名稱的值
				Object valueObj = json.get(fieldName);
				if (valueObj == null) {
					continue;
				}

				// 依照欄位類型寫回 class
				switch (fieldType) {
				case "boolean":
					field.set(tmpObj, (boolean) valueObj);
					break;

				case "String":
					field.set(tmpObj, (String) valueObj);
					break;

				case "int": {
					field.set(tmpObj, (int) ((long) valueObj));
					break;
				}

				case "BigDecimal": {
					BigDecimal var = new BigDecimal((String) valueObj);
					field.set(tmpObj, var);
					break;
				}

				case "Long":
					field.set(tmpObj, (Long) valueObj);
					break;

				case "long":
					field.set(tmpObj, (long) valueObj);
					break;
				
				case "Byte":
					field.set(tmpObj, (Byte) valueObj);
					break;

				case "byte": {
					long value = (long) valueObj;
					field.set(tmpObj, (byte) value);
					break;
				}

				case "Calendar": {
					Calendar cal = Calendar.getInstance();
					cal.setTimeInMillis((long) valueObj);
					field.set(tmpObj, cal);
					break;
				}

				case "Date": {
					Calendar cal = Calendar.getInstance();
					cal.setTimeInMillis((long) valueObj);
					field.set(tmpObj, cal.getTime());
					break;
				}

				case "GUID": {
					GUID guid = GUID.parseUUID((String) valueObj);
					field.set(tmpObj, guid);
					break;
				}

				default:
					if (log.isErrorEnabled()) {
						log.error("toObject(): field [" + tmpObj.getClass().getSimpleName() + "." + fieldName
								+ "] type [" + fieldType + "] not support.");
					}
					break;
				} // switch (fieldType) {
			} // for (Field field : fields) {

			// 取得 parent class
			currentClass = currentClass.getSuperclass();
		} // while (currentClass != null)

		return tmpObj;
	}
}
